﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using Bymed.SpineScan.DataModel;
using Bymed.SpineScan.Repository.Interface;
using Bymed.SpineScan.Service.Shared;
using Bymed.SpineScan.Service.Interface;
using Bymed.SpineScan.Service.Validation;

namespace Bymed.SpineScan.Service
{
    public class TestTrackingService : ServiceBase<TestTracking>, ITestTrackingService
    {
        private ITestTrackingRepository _testTrackingRepository;

        public TestTrackingService(IRepositoryFactory repositoryFactory)
            : base(repositoryFactory, new TestTrackingValidator()) { }

        public override void Delete(TestTracking entity)
        {
            using (RepositoryFactory)
            {
                RepositoryFactory.OpenSession();
                CommonService.DeleteTestTracking(RepositoryFactory, entity.TestTimestamp);
                RepositoryFactory.Commit();
            }
        }

        public override void Delete(IEnumerable<TestTracking> entities)
        {
            using (RepositoryFactory)
            {
                RepositoryFactory.OpenSession();

                foreach (var entity in entities)
                {
                    CommonService.DeleteTestTracking(RepositoryFactory, entity.TestTimestamp);
                }

                RepositoryFactory.Commit();
            }
        }

        /// <summary>
        /// Get all related TestResults by provided patient number and return all rows.
        /// </summary>
        /// <param name="patientNumber">The provided patient number.</param>
        /// <param name="testItemName">The test item name.</param>
        /// <param name="rowCount">How many rows will be returned.</param>
        /// <returns>TestTracking list contains all related TestResults.</returns>
        public IList<TestTracking> GetAllRelatedTestResultsByPatientNumber(string patientNumber, string testItemName)
        {
            return GetAllRelatedTestResultsByPatientNumber(patientNumber, testItemName, null);
        }

        /// <summary>
        /// Get all related TestResults by provided patient number and return the specified rows.
        /// </summary>
        /// <param name="patientNumber">The provided patient number.</param>
        /// <param name="testItemName">The test item name.</param>
        /// <param name="rowCount">How many rows will be returned.</param>
        /// <returns>TestTracking list contains all related TestResults.</returns>
        public IList<TestTracking> GetAllRelatedTestResultsByPatientNumber(string patientNumber, string testItemName, int? rowCount)
        {
            using (RepositoryFactory)
            {
                RepositoryFactory.OpenSession();
                _testTrackingRepository = RepositoryFactory.CreateTestTrackingRepository();

                var result = _testTrackingRepository.Query(t => t.PatientNumber == patientNumber
                    && t.TestResults.Where(r => r.TestItemName == testItemName).Count() == 1, "TestResults")
                    .OrderByDescending(t => t.TestDate);

                if (rowCount.HasValue && rowCount.Value > 0)
                {
                    return result.Take(rowCount.Value).ToList();
                }
                else
                {
                    return result.ToList();
                }
            }
        }

        /// <summary>
        /// Search the test tracking data based on provided TestType.
        /// </summary>
        /// <param name="patient">The patient number.</param>
        /// <param name="testDateType">Selected TestDateType.</param>
        /// <returns>Matched TestTracking data list.</returns>
        public IList<TestTracking> SearchByTestDateType(string patientNumber, TestDateType testDateType)
        {
            using (RepositoryFactory)
            {
                RepositoryFactory.OpenSession();
                _testTrackingRepository = RepositoryFactory.CreateTestTrackingRepository();

                IList<TestTracking> result = new List<TestTracking>();
                Expression<Func<TestTracking, bool>> expression = null;

                switch (testDateType)
                {
                    case TestDateType.All:
                    case TestDateType.Tested:
                        expression = GetAllTestTrackingDataExpression(patientNumber);
                        break;
                    case TestDateType.Testing:
                        expression = GetTestTrackingDataOfTodayExpression(patientNumber);
                        break;
                }

                if (expression != null)
                {
                    result = _testTrackingRepository.Query(expression, 
                        "TestResults;TestResultDiagnosticMessages")
                        .OrderByDescending(t => t.TestDate).ToList();
                }

                return result;
            }
        }

        /// <summary>
        /// Get the Expression to retrieve patient all test data.
        /// </summary>
        /// <param name="patientNumber">The patient number.</param>
        /// <returns>Expression<Func<TestTracking, bool>></returns>
        private Expression<Func<TestTracking, bool>> GetAllTestTrackingDataExpression(string patientNumber)
        {
            return t => t.PatientNumber == patientNumber;
        }

        /// <summary>
        /// Get the Expression to retrieve patient test data in current day.
        /// </summary>
        /// <param name="patientNumber">The patient number.</param>
        /// <returns>Expression<Func<TestTracking, bool>></returns>
        private Expression<Func<TestTracking, bool>> GetTestTrackingDataOfTodayExpression(string patientNumber)
        {
            return t => t.PatientNumber == patientNumber && t.TestDate.Year == DateTime.Now.Year &&
                        t.TestDate.Month == DateTime.Now.Month &&
                        t.TestDate.Day == DateTime.Now.Day;
        }
    }
}
